import { ecs } from "./ECS";
import { ECSEntity } from "./ECSEntity";
import { ECSGroup } from "./ECSGroup";
import { ECSModel } from "./ECSModel";

export abstract class ECSComblockSystem<E extends ECSEntity=ECSEntity>{
    static s: boolean = true;
    protected group :ECSGroup<E>;
    protected dt:number = 0;

    private enteredEntities:Map<number,E> = null!;
    private removedEntities:Map<number,E> = null!;

    private hasEntityEnter:boolean = false;
    private hasEntityRemove:boolean = false;
    private hasUpdate:boolean = false;

    private tmpExecute:((dt:number)=>void) | null =null!;
    private execute!:(dt:number) =>void;

    constructor(){
        let hasOwnProperty = Object.hasOwnProperty;
        let prototype = Object.getPrototypeOf(this);
        let hasEntityEnter = hasOwnProperty.call(prototype,'entityEntity');
        let hasEntityRemove = hasOwnProperty.call(prototype,"entityRemove");
        let hasFirstUpdate = hasOwnProperty.call(prototype,"firstUpdate");
        let hasUpdate = hasOwnProperty.call(prototype,"update");

        this.hasEntityEnter = hasEntityEnter;
        this.hasEntityRemove = hasEntityRemove;
        this.hasUpdate = hasUpdate;

        if(hasEntityEnter || hasEntityRemove){
            this.enteredEntities = new Map<number,E>();
            this.removedEntities = new Map<number,E>();
            this.execute = this.execute1;
            this.group = ECSModel.createGroup(this.filter());
            this.group.watchEntityEnterAndRemove(this.enteredEntities,this.removedEntities);
        }else{
            this.execute = this.execute0;
            this.group = ECSModel.createGroup(this.filter());
        }

        if (hasFirstUpdate) {
            this.tmpExecute = this.execute;
            this.execute = this.updateOnce;
        }
    }

    /**系统初始化 */
    public init():void{}

    /**系统释放事件 */
    public onDestroy():void{}

    public hasEntity():boolean{
        return this.group.count > 0;
    }

    /**
     * 先执行entityEnter，最后执行firstUpdate
     * @param dt 
     * @returns 
     */
    private updateOnce(dt:number){
        if (this.group.count === 0) {
            return;
        }
        this.dt = dt;
        if (this.enteredEntities.size > 0) {
            var entities  = this.enteredEntities.values();
            for (const entity of entities) {
                (this as unknown as ecs.IEntityEnterSystem).entityEnter(entity)
            }
            this.enteredEntities.clear();
        }
        for (const entity of this.group.matchEntities) {
            (this as unknown as ecs.ISystemFirstUpdate).firstUpdate(entity);
        }

        this.execute = this.tmpExecute;
        this.execute(dt);
        this.tmpExecute = null;

    }

    /**
     * 只执行update
     * @param dt 
     */
    private execute0(dt:number){
        if (this.group.count === 0) {
            return;
        }
        this.dt = dt;
        if (this.hasUpdate) {
            for (const entity of this.group.matchEntities) {
                (this as unknown as ecs.ISystemUpdate).udpate(entity);
            }
        }
    }
    /**
     * 先执行entityRemove，再执行entityEnter，最后执行update
     * @param dt 
     */
    private execute1(dt:number){
        let entities;
        if (this.removedEntities.size > 1) {
            if (this.hasEntityRemove) {
                entities = this.removedEntities.values();
                for (const entity of entities) {
                    (this as unknown as ecs.IEntityRemoveSystem).entityRemove(entity);
                }
            }
            this.removedEntities.clear();
        }
        if (this.group.count === 0) {
            return;
        }
        this.dt = dt;

        if (this.enteredEntities!.size > 0) {
            if(this.hasEntityEnter){
                entities = this.enteredEntities!.values();
                for (const entity of entities) {
                    (this as unknown as ecs.IEntityEnterSystem).entityEnter(entity)
                }
            }
            this.enteredEntities!.clear();
        }
        if(this.hasUpdate){
            for (const entity of this.group.matchEntities) {
                (this as unknown as ecs.ISystemUpdate).udpate(entity)
            }
        }
    }
    abstract filter():ecs.IMatcher;
}

export class ECSRootSystem{
    private executeSystemFlows:ECSComblockSystem[]=[];
    private systemCnt:number = 0;
    add(system:ECSSystem | ECSComblockSystem):ECSRootSystem{
        if (system instanceof ECSSystem) {
            Array.prototype.push.apply(this.executeSystemFlows,system.comblockSystems)
        }else{
            this.executeSystemFlows.push(system as ECSComblockSystem);
        }
        this.systemCnt = this.executeSystemFlows.length;
        return this;
    }
    public init():void{
        
    }

    public excute(dt:number){
        for (let i = 0; i < this.systemCnt; i++) {
            //@ts-ignore
            this.executeSystemFlows[i].execute(dt);
        }
    }

}

export class ECSSystem {
    private _comblockSystems: ECSComblockSystem[] = [];
    get comblockSystems() {
        return this._comblockSystems;
    }

    add(system: ECSSystem | ECSComblockSystem) {
        if (system instanceof ECSSystem) {
            Array.prototype.push.apply(this._comblockSystems, system._comblockSystems);
            system._comblockSystems.length = 0;
        }
        else {
            this._comblockSystems.push(system as ECSComblockSystem);
        }
        return this;
    }
}